package com.spring.jackson.seckill.service;

import com.spring.jackson.message.Message;
import com.spring.jackson.message.MessageTrunk;
import com.spring.jackson.seckill.cache.*;
import com.spring.jackson.seckill.constant.MessageType;
import com.spring.jackson.seckill.dao.GoodsMapper;
import com.spring.jackson.seckill.model.Goods;
import com.spring.jackson.seckill.mq.message.SeckillRequestMessage;
import com.spring.jackson.smvc.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class GoodsService {

    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private SeckillFinishCache seckillinishCache;
    @Autowired
    private GoodsBuyCurrentLimiter goodsBuyCurrentLimiter;
    @Autowired
    private SeckillHandlingListCache seckillHandlingListCache;
    @Autowired
    private MessageTrunk messageTrunk;
    @Autowired
    private GoodsInfoCacheWorker goodsInfoCacheWorker;
    @Autowired
    private SeckillSuccessTokenCache seckillSuccessTokenCache;

    public void seckill(String mobile, String goodsRandomName) {

        // 先看抢购是否已经结束了
        if (seckillinishCache.isFinish(goodsRandomName)) {
            throw new BusinessException("您已经提交抢购，正在处理中");
        }

        //限流
        goodsBuyCurrentLimiter.doLimit(goodsRandomName, "啊呀，没挤进去");

        // 判断是否处理中(是否在处理列表中)
        if (seckillHandlingListCache.isInHanleList(mobile, goodsRandomName)) {
            throw new BusinessException("您已经提交过抢购，如果抢购成功请下单，否则耐心等待哦...");
        }

        // 请求消息推入处理队列，结束，这里的“MessageType.MIAOSHA_MESSAGE”就相当于rabbitmq的队列名称
        Message message = new Message(MessageType.MIAOSHA_MESSAGE, new SeckillRequestMessage(mobile, goodsRandomName));
        messageTrunk.put(message);

        //将消息加入正在处理列表
        seckillHandlingListCache.add2HanleList(mobile, goodsRandomName);

    }


    /**
     * 真正的减库存，下单
     *
     * @param mobile
     * @param goodsId
     * @param token
     * @return
     */
    public Integer order(String mobile, Integer goodsId, String token) {
        // 先检查token有效性
        Goods goodsInfo = goodsInfoCacheWorker.get(goodsId, Goods.class);
        if (!seckillSuccessTokenCache.validateToken(mobile, goodsInfo.getRandomName(), token)) {
            throw new BusinessException("token不对，不能下单哦");
        }
        // 先检查库存，没有库存直接结束
        checkStore(goodsInfo.getRandomName());

        return reduceStoreAndCreateOrder(mobile, goodsId);

    }

    /**
     * 检查库存
     *
     * @param goodsRandomName
     * @return
     */
    private Goods checkStore(String goodsRandomName) {
        Goods goods = goodsMapper.selectByRandomName(goodsRandomName);
        if (goods == null || goods.getStore().intValue() <= 0) {
            //在redis设置此商品秒杀结束
            seckillinishCache.setFinish(goodsRandomName);
            throw new RuntimeException("很遗憾，抢购已经结束了哟"); // 库存不足，抢购失败
        }
        return goods;
    }

    /**
     * 真正做减库存操作(这里没有采用存储过程的原因：这里并没有高并发，高并发已经在获取token时分流，所以此处没必要用存储过程)
     *
     * @param mobile
     * @param goodsId
     * @return
     */
    public Integer reduceStoreAndCreateOrder(String mobile, Integer goodsId) {
        Date orderTime = new Date();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("goodsId", goodsId);
        map.put("mobile", mobile);
        map.put("orderTime", orderTime);
        map.put("o_result", -2);
        map.put("o_order_id", -1);
        //调用存储过程,自定义mysql函数实现创建订单+扣减库存
        goodsMapper.doOrder(map);

        Integer result = (Integer) map.get("o_result");
        if (null != result && result == 1) {
            return (Integer) map.get("o_order_id");
        }

        throw new BusinessException("下单失败，来晚了");
    }

    /**
     * 获取抢购商品列表
     *
     * @return
     * @category 获取抢购商品列表
     */
    public List<Goods> getGoodsList() {
        return goodsMapper.selectAll();
    }

    /**
     * 获取商品详情
     *
     * @param goodsId
     * @return
     * @category 获取商品详情
     */
    public Goods getDetail(Integer goodsId) {
        return goodsMapper.selectByPrimaryKey(goodsId);
    }
}
